package com.ruoyi.ade.tool;

import com.alibaba.fastjson2.JSONArray;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class JaccardStringSimilarity {

    /**
     * 计算两个字符串的Jaccard相似度（基于n-gram）
     * @param s1 第一个字符串
     * @param s2 第二个字符串
     * @param n n-gram的大小
     * @return Jaccard相似度（0-1之间）
     */
    public static double jaccardSimilarity(String s1, String s2, int n) {
        if (s1 == null || s2 == null) {
            return 0.0;
        }

        Set<String> grams1 = generateNGrams(s1, n);
        Set<String> grams2 = generateNGrams(s2, n);

        // 计算交集大小
        Set<String> intersection = new HashSet<>(grams1);
        intersection.retainAll(grams2);
        int intersectionSize = intersection.size();

        // 计算并集大小
        Set<String> union = new HashSet<>(grams1);
        union.addAll(grams2);
        int unionSize = union.size();

        // 避免除以零
        if (unionSize == 0) {
            return 0.0;
        }

        return (double) intersectionSize / unionSize;
    }

    /**
     * 生成n-grams集合
     */
    private static Set<String> generateNGrams(String s, int n) {
        Set<String> grams = new HashSet<>();
        if (s.length() < n) {
            return grams;
        }

        for (int i = 0; i <= s.length() - n; i++) {
            grams.add(s.substring(i, i + n));
        }
        return grams;
    }

    /**
     * 判断两个字符串是否相似（基于Jaccard相似度阈值）
     * @param s1 第一个字符串
     * @param s2 第二个字符串
     * @param n n-gram大小
     * @param threshold 相似度阈值（0-1之间）
     * @return 是否相似
     */
    public static boolean isSimilar(String s1, String s2, int n, double threshold) {
        double similarity = jaccardSimilarity(s1, s2, n);
        return similarity >= threshold;
    }

    /**
     * 增强版：使用多种n-gram组合计算相似度（更鲁棒）
     */
    public static double enhancedJaccardSimilarity(String s1, String s2, int[] nSizes) {
        double totalSimilarity = 0.0;
        for (int n : nSizes) {
            totalSimilarity += jaccardSimilarity(s1, s2, n);
        }
        return totalSimilarity / nSizes.length;
    }

    /**
     * 基于单词的Jaccard相似度（需要先分词）
     */
    public static double jaccardSimilarityByWords(String s1, String s2) {
        Set<String> words1 = splitIntoWords(s1);
        Set<String> words2 = splitIntoWords(s2);

        Set<String> intersection = new HashSet<>(words1);
        intersection.retainAll(words2);
        int intersectionSize = intersection.size();

        Set<String> union = new HashSet<>(words1);
        union.addAll(words2);
        int unionSize = union.size();

        if (unionSize == 0) {
            return 0.0;
        }

        return (double) intersectionSize / unionSize;
    }

    /**
     * 简单分词方法（按中文常见分隔符）
     */
    private static Set<String> splitIntoWords(String s) {
        // 可以根据需要调整分词规则
        return new HashSet<>(Arrays.asList(s.split("[，,。.；;！!？?、\\s]+")));
    }

    public static void main(String[] args) {
        String s1 = "检查后患者突然出现头痛头晕不适，随后出现全身乏力，摔倒在地，随后CT室值班医师立即查看患者，患者神志模糊，呼之可睁眼，言语声小，查血压160/80mmHg,四肢冰凉，考虑药物相关性反应可能，";
        String s2 = "患者今日上午10时左右于CT室行CTA检查，检查后患者突然出现头痛头晕不适，随后出现全身乏力，摔倒在地，考虑药物相关性反应可能";

        System.out.println(JSONArray.toJSONString(splitIntoWords(s1)));
        System.out.println(JSONArray.toJSONString(splitIntoWords(s2)));
        // 测试n-gram方法
        int n = 4; // 可以尝试3,4,5等不同值
        double similarity = jaccardSimilarity(s1, s2, n);
        System.out.printf("Jaccard相似度(n=%d): %.4f%n", n, similarity);

        // 测试增强版
        int[] nSizes = {3, 4, 5};
        double enhancedSimilarity = enhancedJaccardSimilarity(s1, s2, nSizes);
        System.out.printf("增强版Jaccard相似度: %.4f%n", enhancedSimilarity);

        // 测试基于单词的相似度
        double wordSimilarity = jaccardSimilarityByWords(s1, s2);
        System.out.printf("基于单词的Jaccard相似度: %.4f%n", wordSimilarity);

        // 判断是否相似
        double threshold = 0.3; // 可以根据需要调整
        boolean isSimilar = isSimilar(s1, s2, n, threshold);
        System.out.printf("是否相似(阈值=%.2f): %s%n", threshold, isSimilar);
    }
}